package server;

import factory.MyFactory;
import log.LogService;
import log.LogServiceImpl;

import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 服务端配置类
 */
public class ServerConfig {

    private ServerSocket serverSocket;

    private LogService log = MyFactory.createLogService();

    private final int CORE_POOL_SIZE = 1000;

    private final int MAXIMUM_POOL_SIZE = 2000;

    private final int KEEP_ALIVE_TIME = 600;

    // 存放所有客户端的输出流，实现广播通知
    private List<PrintWriter> clientNotices = new ArrayList<>();

    public ServerConfig() {
        // 在构造方法中申请端口
        try {
            log.ok("服务端开始启动");
            serverSocket = new ServerSocket(8094);
            log.ok("服务端启动成功");
        } catch (Exception e) {
            if (e instanceof BindException) {
                // 端口冲突
                log.error("服务端申请端口时出现端口冲突异常");
            } else {
                // 其他异常
                log.error("服务端申请端口时出现其他异常");
            }
            e.printStackTrace(); // 打印异常信息
        }
    }

    /**
     * 启动方法
     */
    public void start() {
        try {
            while (true) {
                log.ok("服务端启动，等待客户端连接");

                Socket accept = serverSocket.accept(); // 监听是否有客户端进行连接，若不连接则一直阻塞
                ClientHandlerImpl clientHandler = new ClientHandlerImpl(accept); // 将接收到的socket传给多线程处理器进行处理

                // 创建线程，开启多线程聊天功能
                MyFactory factory = new MyFactory();
                // 服务端使用线程池来管理线程
                ExecutorService pool = factory.createThreadPool(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME);
                pool.execute(clientHandler);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 实现多线程接收客户端消息
     */
    private class ClientHandlerImpl implements Runnable {
        private Socket socket;

        private String host;

        private Lock lock = new ReentrantLock();

        public ClientHandlerImpl(Socket socket) {
            this.socket = socket;
            host = socket.getInetAddress().getHostAddress();
        }

        @Override
        public void run() {
            PrintWriter printWriter = null;
            try {
                // 通过socket获取输入流
                InputStream inputStream = socket.getInputStream();
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                // 通过socket获取输出流
                OutputStream outputStream = socket.getOutputStream();
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
                printWriter = new PrintWriter(bufferedWriter, true);

                // 将输出流存入共享集合，实现广播
                lock.lock();
                clientNotices.add(printWriter);
                lock.unlock();
                log.ok(host + "已上线，系统在线人数：" + clientNotices.size());

                // 接收客户端消息
                String clientMessage;
                while ((clientMessage = bufferedReader.readLine()) != null) {
                    log.ok(host + "发布了消息：" + clientMessage); // 为服务端记录日志
                    synchronized (clientNotices) {
                        for (PrintWriter clientNotice : clientNotices) {
                            // 将消息同步显示在客户端
                            clientNotice.println(host + "发布了消息：" + clientMessage);
                        }
                    }
                }
            } catch (Exception e) {
                if (e instanceof SocketException) {
                    // 有客户出现连接异常，当未进行TCP四次挥手就断开连接，会抛此异常
                    log.warning("客户连接异常");
                } else {
                    log.error("服务端接收消息出现异常，该异常也可能来自客户端");
                }
                e.printStackTrace();
            } finally {
                // 若客户端断开连接，则需将当前客户端从集合中删除
                lock.lock();
                clientNotices.remove(printWriter);
                lock.unlock();
                log.ok(host + "已下线，系统当前人数：" + clientNotices.size());

                // 关闭此套接字
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}
